#include "obj_list.h"
#include "utils.h"
#include "header_obj.h"

/**
 * @brief 新建list对象，元素个数为element_num
*/
ObjList* new_ObjList(VM *vm, uint32_t element_num)
{
    Value *element_array = NULL;
    // 先分配内存，后调用initobjHeader，避免gc无谓的遍历
    if (element_num > 0)
    {
        element_array = ALLOCATE_ARRAY(vm, Value, element_num);
    }
    ObjList *obj_list = ALLOCATE(vm, ObjList);

    obj_list->elements.datas = element_array;
    obj_list->elements.capacity = obj_list->elements.count = element_num;
    init_objheader(vm, &obj_list->obj_header, OT_LIST, vm->list_class);
    return obj_list;
}

/**
 * @brief 删除list中索引为index处的元素
*/
Value remove_element(VM *vm, ObjList *obj_list, uint32_t index)
{
    Value value_removed = obj_list->elements.datas[index];

    // 使index后面的元素前移一位，覆盖index处的元素
    uint32_t idx = index;
    while (idx < obj_list->elements.count)
    {
        obj_list->elements.datas[idx] = obj_list->elements.datas[idx + 1];
        idx ++;
    }

    // 若容量利用率过低就减小容量
    uint32_t _capacity = obj_list->elements.capacity / CAPACITY_GROW_FACTOR;
    if (_capacity > obj_list->elements.count)
    {
        shrink_list(vm, obj_list, _capacity);
    }

    obj_list->elements.count --;
    return value_removed;
}

/**
 * @brief 在objlist中索引为index处插入value，类似于list[index]=value
*/
void insert_element(VM *vm, ObjList *obj_list, uint32_t index, Value value)
{
    if (index > obj_list->elements.count - 1)
    {
        RUNTIME_ERROR("index out bounded!");
    }
    // 准备一个value的框架以容纳新元素产生的空间波动
    // 即最后一个元素要后移1个空间
    Value_buffer_add(vm, &obj_list->elements, VT_TO_VALUE(VT_NULL));

    // 下面使index后面的元素整体后移一位
    uint32_t idx = obj_list->elements.count - 1;
    while (idx > index)
    {
        obj_list->elements.datas[idx] = obj_list->elements.datas[idx - 1];
        idx --;
    }

    // 在index插入数值
    obj_list->elements.datas[index] = value;
}

/**
 * @brief 调整list容量
*/
static void shrink_list(VM *vm, ObjList *obj_list, uint32_t new_capacity)
{
    uint32_t old_size = obj_list->elements.capacity * sizeof(Value);
    uint32_t new_size = new_capacity * sizeof(Value);

    mem_manager(vm, obj_list->elements.datas, old_size, new_size);
    obj_list->elements.capacity = new_capacity;
}